﻿//-----------------------------------------------------------------------
// <copyright file="CultureManager.cs" company="WpfDevelopment.com">
//     Copyright WpfDevelopment.com. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace WpfDevelopment.MVVM
{
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Diagnostics.Contracts;
    using System.Dynamic;
    using System.Globalization;
    using System.Reflection;
    using System.Resources;
    using System.Threading;

    /// <summary>
    /// TODO Refactor  comment etc.
    /// </summary>
    public class CultureManager : DynamicObject, INotifyPropertyChanged, ICultureManager
    {
        /// <summary>
        /// the resource manager
        /// </summary>
        private IResourceManager resourceManager;
        
        /// <summary>
        /// The logger instance
        /// </summary>
        private ILog log;

        /// <summary>
        /// Initializes a new instance of the <see cref="CultureManager"/> class.
        /// </summary>
        /// <param name="type">The type of viewmodel to get resources for.</param>
        /// <param name="communicationBus">The communication bus.</param>
        /// <param name="log">The log.</param>
        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "code contracts"), 
         SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "code contracts"), 
         SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "3", Justification = "code contracts")]
        public CultureManager(Type type, ICommunicationBus communicationBus, ILog log, IResourceManager resourceManager)
        {
            Contract.Requires<ArgumentNullException>(type != null);
            Contract.Requires<ArgumentNullException>(communicationBus != null);
            Contract.Requires<ArgumentNullException>(log != null);
            Contract.Requires<ArgumentNullException>(resourceManager != null);

            this.log = log;
            this.resourceManager = resourceManager;
            this.resourceManager.Init(type.Namespace + ".CultureResources" + "." + type.Name, Assembly.GetAssembly(type));

            this.CultureChangedEvent += (s, e) =>
            {
                ResourceSet set = this.resourceManager.GetResourceSet(CultureInfo.CurrentCulture, true, true);
                if (set != null)
                {
                    foreach (DictionaryEntry o in set)
                    {
                        PropertyChanged.Invoke(this, new PropertyChangedEventArgs((string)o.Key));
                    }

                    this.resourceManager.ReleaseAllResources();
                }
            };

            Action<object> callback =
                c =>
                {
                    if (!Thread.CurrentThread.CurrentUICulture.Name.Equals(c.ToString(), StringComparison.OrdinalIgnoreCase))
                    {
                        Thread.CurrentThread.CurrentUICulture = new CultureInfo((string)c);
                        this.CultureChangedEvent.Invoke(this, new EventArgs());
                    }
                };

            if (callback != null && callback.Target != null)
            {
                communicationBus.Register("ChangeCulture", callback);
            }
        }

        /// <summary>
        /// Occurs when the culture changed event.
        /// </summary>
        public event EventHandler<EventArgs> CultureChangedEvent;

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Provides the implementation for operations that get member values. 
        /// Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> 
        /// class can override this method to specify dynamic behavior for 
        /// operations such as getting a value for a property.
        /// </summary>
        /// <param name="binder">Provides information about the object that called 
        /// the dynamic operation. The binder.Name property provides the name of 
        /// the member on which the dynamic operation is performed. For example, 
        /// for the Console.WriteLine(sampleObject.SampleProperty) statement, 
        /// where sampleObject is an instance of the class derived from the
        /// <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Name returns "SampleProperty". 
        /// The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param>
        /// <param name="result">The result of the get operation. For example, if the method is
        /// called for a property, you can assign the property value to <paramref name="result"/>.</param>
        /// <returns>
        /// true if the operation is successful; otherwise, false. If this method returns
        /// false, the run-time binder of the language determines the behavior. (In most cases, a run-time exception is thrown.)
        /// </returns>
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            if (binder == null)
            {
                result = null;
                return false;
            }

            // The resource manager to use
            ResourceManager stringManager = new ResourceManager("WpfDevelopment.MVVM.CultureManager", this.GetType().Assembly);

            try
            {
                result = this.resourceManager.GetString(binder.Name);
                return true;
            }
            catch (ArgumentNullException)
            {
                result = string.Empty;
                this.log.Write(stringManager.GetString("ParameterCannotBeNull", CultureInfo.CurrentUICulture));
                return false;
            }
            catch (InvalidOperationException)
            {
                result = string.Empty;
                this.log.Write(string.Format(stringManager.GetString("ResourceRequestedIsNotOfTypeString", CultureInfo.CurrentUICulture), binder.Name));
                return false;
            }
            catch (MissingManifestResourceException) 
            {
                result = string.Empty;
                this.log.Write(stringManager.GetString("NeutralCultureCannotBeFound", CultureInfo.CurrentUICulture));
                return false;
            }
        }
    }
}
